import time
import random
import math
import sys

##############################
##  *** Global Settings *** ##
####### *************** ######
##############################

##############
# Parameters #
##############
# Number of agents that make a spot crowded.
CROWDED_HOTSPOT = 3
# Range in pixels that still counts as the same hotspot.
HOTSPOT_RANGE = 20
# The number of turns before a control point is considered peaceful.
DOMINATION_THRESHOLD = 5
# Number of ammo that counts as enough.
SUFFICIENT_AMMO = 2
# Range around enemy base
ENEMY_BASE_RANGE = 50
# Range for getting ammo near enemy base
FIND_AMMO_RANGE = 100
# Size of state description grid
GRID_SIZE = 100

###################
# World knowledge #
###################
NUM_AMMO_SPOTS = 6
DEFAULT_FIELD_TILESIZE = 16 # in case not provided in settings
DEFAULT_FIELD_WIDTH = 41    # in case not provided in settings
DEFAULT_FIELD_HEIGHT = 26   # in case not provided in settings

#####################
# Behavior settings #
#####################
# Agents who died don't think about their destinations
SETTINGS_DEAD_CANT_THINK = True
SETTINGS_DONT_HARM_FRIENDS = True

####################
# Feature settings #
####################
# The domination value is cumulative.
SETTINGS_DOMINATION_ADDS_UP = True

##################
# Debug settings #
##################
SETTINGS_DEBUG_ON = False
SETTINGS_DEBUG_ERROR_ONLY = False
SETTINGS_DEBUG_SHOW_VISIBLE_OBJECTS = True
SETTINGS_DEBUG_SHOW_VISIBLE_FOES = True
SETTINGS_DEBUG_SHOW_ID = True
SETTINGS_DEBUG_SHOW_MOTIVATION = True
SETTINGS_DEBUG_SHOW_AMMO = True
SETTINGS_DEBUG_SHOW_DOMINATION = True
SETTINGS_DEBUG_SHOW_KNOWN_AMMO_SPOTS = True
SETTINGS_DEBUG_SHOW_BASES = True
SETTINGS_DEBUG_SHOW_GRIDLINES = True

###############################
# State description constants #
###############################
SD_CELL_SHIFT = 22
SD_CELL_START_SHIFT = 51
   ##################
   ## General info ##
   ##################
SD_SCORE_SHIFT = 0          # 3 bit
SD_PREVIOUS_ROLE = 3        # 24 bit
SD_DVALUECPS_SHIFT = 27     # 6 bit
SD_FRIENDS_AMMO_SHIFT = 33  # 18 bit
   ###################
   ## Per Cell info ############
   # Shifts are relative to    #
   # the beginning of the cell #
   #############################
SD_FRIENDS_SHIFT = 0      # 6 bit
SD_ENEMIES_SHIFT = 6      # 3 bit
SD_ENEMYCPS_SHIFT = 9     # 3 bit
SD_FRIENDCPS_SHIFT = 12   # 3 bit
SD_AMMO_SHIFT = 15        # 3 bit
SD_DEATH_RATE = 18        # 2 bit
SD_ENEMYBASE_SHIFT = 20   # 1 bit
SD_HOMEBASE_SHIFT = 21    # 1 bit

########################
# Motivation constants #
########################
# Motivation: Capture a enemy control point
MOTIVATION_CAPTURE_CP = 'C'
# Motivation: Guard a friendly control point
MOTIVATION_GUARD_CP = 'G'
# Motivation: Pickup ammo pack
MOTIVATION_AMMO = 'A'
# Motivation: Wait for ammo on spot
MOTIVATION_AMMO_SPOT = 'AS'
# Motivation: User clicked
MOTIVATION_USER_CLICK = 'U'
# Motivation: Shoot an enemy
MOTIVATION_SHOOT_TARGET = 'S'
# Motivation: Go to enemy base
MOTIVATION_ENEMY_BASE = 'EB'
# Motivation: Stay at current location
MOTIVATION_STAY_PUT = 'SP'
# Motivation: nothing to do: wander around
MOTIVATION_IDLE = 'I'
#Motivation: flee from enemies
MOTIVATION_FLEE = 'F'

######################
# Strategy constants #
#    [Deprecated]    #
######################
STRATEGY_NORMAL = 'N'
STRATEGY_DEFENCE = 'D'
STRATEGY_OFFENCE = 'O'
ROLE_CAPTURE = 'C'
ROLE_CHASE = 'E'
ROLE_AMMO = 'A'
ROLE_FLEE = 'F'
ROLE_SCOUT = 'S'

##################################
# Strategies based on game_state #
##################################

# Read as follows:
# [list of roles for game state 0, game state 1, game state 2, ...]
# Each such list consists of roles of agent 0 to 5.

# Changes from N in losing states to O in winning states
#GAME_STATE_STRATEGIES = [['N','N','N','N','N','N'], ['N','N','N','N','D','O'], ['N','N','D','D','O','O'], ['N','D','D','O','O','O'], ['D','D','O','O','O','O']]

# Default NNDDOO strategy in all game states
#GAME_STATE_STRATEGIES = [['N','N','D','D','O','O'], ['N','N','D','D','O','O'], ['N','N','D','D','O','O'], ['N','N','D','D','O','O'], ['N','N','D','D','O','O']]

# Offensive only in all states!
#GAME_STATE_STRATEGIES = [['O','O','O','O','O','O'], ['O','O','O','O','O','O'], ['O','O','O','O','O','O'], ['O','O','O','O','O','O'], ['O','O','O','O','O','O']]

# N only
#GAME_STATE_STRATEGIES = [['N','N','N','N','N','N'], ['N','N','N','N','N','N'], ['N','N','N','N','N','N'], ['N','N','N','N','N','N'], ['N','N','N','N','N','N']]

# D only
GAME_STATE_STRATEGIES = [['D','D','D','D','D','D'], ['D','D','D','D','D','D'], ['D','D','D','D','D','D'], ['D','D','D','D','D','D'], ['D','D','D','D','D','D']]

########################
# Q Learning Constants #
########################
Q_LEARNING = False
GAMMA = 0.99
EPSILON = 0.1
ALPHA = 1
Q = {}

################
# Set of roles #
################
ROLES_SET = [ "O", "D", "N" ]
ROLES = {
  "O": lambda o: o.action_offence,
  "D": lambda o: o.action_defend,
  "N": lambda o: o.action_normal,
  "C": lambda o: o.role_captureCP,
  "E": lambda o: o.role_chaseNearestEnemy,
  "A": lambda o: o.role_getAmmo,
  "F": lambda o: o.role_flee,
  "S": lambda o: o.scout
}

class Agent(object):
  
  NAME = "Trooper"
  
  PREVIOUS_STATE = None
  PREVIOUS_ACTION = None
  PREVIOUS_REWARD = 0

  #########################
  # Extended observations #
  #########################

  # Location of the bases
  home_base = None
  enemy_base = None

  # Mapping between locations
  # and agents who are going there.
  trendingSpot = {}

  # Mapping between friendly CPs
  # and the amount of steps it has
  # been in friendly hands
  inFriendlyHands = {}

  # List of CPs in friendly hands
  friendlyCPs = []

  # List of CPs in enemy hands
  enemyCPs = []

  # Ammo locations
  ammoSpots = []

  # Visible ammo
  ammo = []
  
  friends = []

  foes = []
  
  hitlist = []
  
  joint_goal = []

  # State of the game based on the score (values: 0-4, where 0 is losing badly and 4 is almost winning)
  game_state = 2

  ##################
  # Initialization #
  ##################
    
  def __init__(self, id, team, settings=None, field_rects=None, field_grid=None, nav_mesh=None, blob=None):
    """ Each agent is initialized at the beginning of each game.
        The first agent (id==0) can use this to set up global variables.
        Note that the properties pertaining to the game field might not be
        given for each game.
    """
    if SETTINGS_DEBUG_ON:
      self.log = open(("log%d.txt" % id),"w")
    '''
    if Q_LEARNING and blob is not None:
      try:
        Q = pickle.loads(blob.read())
        self.debugMsg("Q table loaded with %d states" % (len(Q),))
      except Exception, ex:
        self.debugMsg("Exception: %s - %s" % (ex,sys.exc_info()), True)
      blob.seek(0)'''
        
    self.id = id
    self.team = team
    self.mesh = nav_mesh
    self.grid = field_grid
    self.settings = settings
    self.motivation = None
    self.goal = None
    self.a = None
    self.a_previous = None

    # Recommended way to share variables between agents.
    if id == 0:
      self.all_agents = self.__class__.all_agents = []
      
      tilesize = getattr(self.settings, 'tilesize', DEFAULT_FIELD_TILESIZE)
      if field_rects is None:
        self.__class__.field_width = DEFAULT_FIELD_WIDTH*tilesize
        self.__class__.field_height = DEFAULT_FIELD_HEIGHT*tilesize
      else:
        self.__class__.field_width = len(field_grid[0])*tilesize
        self.__class__.field_height = len(field_grid)*tilesize

    self.all_agents.append(self)
  
  ############################
  # *  Observation methods * #
  ############################

  def observe(self, obs):
    """ Each agent is passed an observation using this function,
        before being asked for an action. You can store either
        the observation object or its properties to use them
        to determine your action. Note that the observation object
        is modified in place.
    """
    self.observation = obs
    self.selected = obs.selected
    
    if Q_LEARNING:
      self.__class__.PREVIOUS_REWARD = self.__class__.PREVIOUS_REWARD + self.determineReward(obs)
    
    if self.id == 0:
      
      # Reset trendingSpot
      self.__class__.trendingSpot = {}

      # Reset friend locations
      self.__class__.friends = []

      # Reset (visible) enemy locations
      self.__class__.foes = []

      # Reset (visible) ammo
      self.__class__.ammo = []

      # Reset hitlist
      self.__class__.hitlist = []
      
      # reset goals
      self.__class__.joint_goal = []
    
    # Communicate my location
    self.__class__.friends.append((obs.loc[0], obs.loc[1], obs.ammo, self.id))
    
    # Communicate visible enemies
    for foe in obs.foes:
      if foe[0:2] not in self.foes:
        self.__class__.foes.append(foe[0:2])
      
    # Update ammo packs  
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    if ammopacks:
      self.updateAllAmmoSpots(ammopacks)
    
    #############################
    # Update of turn statistics #
    #############################
    if self.id == (obs.step % 6):
      # Store base locations
      if self.__class__.home_base is None:
        self.__class__.home_base = (obs.loc[0]+16, obs.loc[1]+8)
        self.__class__.enemy_base = \
          self.getSymmetricOpposite(self.__class__.home_base)
    
      # Update friendly CPs
      self.__class__.friendlyCPs = map(lambda x: x[0:2], 
        filter(lambda x: x[2] == self.team, obs.cps))
      
      # Update enemy CPs
      self.__class__.enemyCPs = map(lambda x:x[0:2], 
        filter(lambda x: x[2] != self.team, obs.cps))
    
      # Update inFriendlyHands stat
      if SETTINGS_DOMINATION_ADDS_UP:
        inFriendlyHands = self.__class__.inFriendlyHands
      else:
        inFriendlyHands = {}
      for cp in self.__class__.friendlyCPs:
        if cp in self.__class__.inFriendlyHands:
          inFriendlyHands[cp] = self.__class__.inFriendlyHands[cp] + 1
        else:
          inFriendlyHands[cp] = 1
      self.__class__.inFriendlyHands = inFriendlyHands

      # Update the 'state of the game' based on the current score
      score = obs.score[self.team]

      if score < 200:
        self.__class__.game_state = 0
      elif score < 450:
        self.__class__.game_state = 1
      elif score < 550:
       self.__class__.game_state = 2
      elif score < 800:
       self.__class__.game_state = 3
      else:
       self.__class__.game_state = 4

  def determineReward(self, obs):
    if self.a != self.a_previous:
      return -1
    elif self.goal is not None:
      if self.motivation == MOTIVATION_CAPTURE_CP:
        if self.goal in self.__class__.friendlyCPs:
          return -10
      elif self.motivation == MOTIVATION_GUARD_CP:
        if self.goal not in self.__class__.enemyCPs:
          return 20
      elif self.motivation == MOTIVATION_AMMO:
        if ((self.goal[0], self.goal[1], 'Ammo') not in obs.objects):
          return -10
        elif self.prev_ammo < obs.ammo:
          return 10
      elif self.motivation == MOTIVATION_AMMO_SPOT:
        if self.getClosestLocation(self.ammo) != self.goal:
          return -10
        elif self.prev_ammo < obs.ammo:
          return 5
      elif self.motivation == MOTIVATION_IDLE:
        if self.goal in self.__class__.enemyCPs:
          return 5
        elif self.goal in self.__class__.friendlyCPs:
          return -1
      elif self.motivation == MOTIVATION_SHOOT_TARGET:
        if obs.hit == self.team:
          return -50
        elif obs.hit != self.team:
          return 50
        else:
          return -50
      elif self.motivation == MOTIVATION_ENEMY_BASE:
        return 1
      elif self.motivation == MOTIVATION_FLEE:
        return 1
      elif self.motivation == MOTIVATION_STAY_PUT:
        return 1
      else:
        return 0
      return 0
    return 0
      
  # Returns the opposite coordinate given the
  # symmetric property of the field
  def getSymmetricOpposite(self, coord):
    mid = round(self.__class__.field_width/2.0 + 0.5)
    if coord[0] > mid:
      return (int(mid-(coord[0]-mid)), int(coord[1]))
    else:
      return (int(mid+(mid-coord[0])), int(coord[1]))

  # Registers goal in trending spot dictionary
  def updateTrendingSpot(self):
    if self.goal:
      if self.goal in self.__class__.trendingSpot:
        self.__class__.trendingSpot[self.goal].append(self.id)
      else:
        self.__class__.trendingSpot[self.goal] = [self.id]

  # Registers (unknown) ammo spots
  def updateAllAmmoSpots(self, spots):
    if len(self.ammo) < NUM_AMMO_SPOTS:
      for spot in spots:
        self._updateAmmoSpots(spot)
        self._updateAmmo(spot)
        self._updateAmmoSpots(self.getSymmetricOpposite(spot))

  # Auxilary method of updateAllAmmoSpots
  def _updateAmmoSpots(self, spot):
    if spot[0:2] not in self.__class__.ammoSpots:
      self.__class__.ammoSpots.append(spot[0:2])

  def _updateAmmo(self, ammo):
    if ammo[0:2] not in self.__class__.ammo:
      self.__class__.ammo.append(ammo[0:2])
  
  #########################
  # * Feature retrieval * #
  #########################

  def getStateDescription(self):
    # Init SD
    sd = 0
    
    # Update friends information
    fs = self.getOrderedFriendLocations()
    for f in xrange(len(fs)):
      # Update encoding of friends that are present in this cell
      sd = self.updateSDCell(
        sd,                         # State description
        fs[f][0],                   # X Coordinate of friend
        fs[f][1],                   # Y Coordinate of friend
        1,                          # Value (Boolean)
        (SD_FRIENDS_SHIFT + f)      # Shift (Category + friend id)
      )
      # Update friends ammo stats
      if fs[f][2] > 6:
        ammo_value = 7
      else:
        ammo_value = fs[f][2]
      sd = self.updateSD(
        sd,
        ammo_value,
        SD_FRIENDS_AMMO_SHIFT + (f*3)
      )

    fs = self.foes
    for f in xrange(len(fs)):
      # Update encoding of enemies that are present in this cell
      sd = self.updateSDCell(
        sd,                         # State description
        fs[f][0],                   # X Coordinate of enemy
        fs[f][1],                   # Y Coordinate of enemy
        1,                          # Value (Boolean)
        (SD_ENEMIES_SHIFT + f)      # Shift (Category + enemy id)
      )
    
    ammo = self.ammo
    for a in xrange(len(ammo)):
      sd = self.addSDCell(
        sd,
        ammo[a][0],
        ammo[a][1],
        1,
        SD_AMMO_SHIFT
      )
    
    # Update control points information
    cps = self.observation.cps
    for cp in xrange(len(cps)):
      if cps[cp][2] == self.team:
        # Update encoding of friendly CPs in this cell
        sd = self.updateSDCell(
          sd,
          cps[cp][0],
          cps[cp][1],
          1,
          (SD_FRIENDCPS_SHIFT + cp)
        )
      else:
        # Update encoding of hostile CPs in this cell
        sd = self.updateSDCell(
          sd,
          cps[cp][0],
          cps[cp][1],
          1,
          (SD_ENEMYCPS_SHIFT + cp)
        )
      # Update encoding of domination values of CPs in this cell
      # Domination values are encoded into 4 groups (0,1,2,3)
      sd = self.updateSD(
        sd,
        int(self.getDominationValue(cps[cp][0:2])/0.25), 
        (SD_DVALUECPS_SHIFT + (cp*2))
      )

    # Update home base
    sd = self.updateSDCell(
      sd, 
      self.home_base[0],
      self.home_base[1],
      1,
      SD_HOMEBASE_SHIFT
    )

    # Update enemy base
    sd = self.updateSDCell(
      sd, 
      self.enemy_base[0],
      self.enemy_base[1],
      1,
      SD_ENEMYBASE_SHIFT
    )

    # Update score information
    sd = self.updateSD(
      sd,
      (self.observation.score[int(self.team==TEAM_BLUE)]/250),
      SD_SCORE_SHIFT
    )
    
    # Update previous action
    prevRoles = 0
    sd = self.updateSD(
      sd,
      prevRoles,
      SD_PREVIOUS_ROLE
    )

    return sd

  def getCellByXY(self, x, y):
    return (int(x)/GRID_SIZE) + ((int(y)/GRID_SIZE)*(((self.field_width-1)/GRID_SIZE)+1))

  def addSDCell(self, sd, x, y, value, shift):
    return self.addSD(
      sd, 
      value, 
      shift + SD_CELL_START_SHIFT + ( self.getCellByXY(x, y) * SD_CELL_SHIFT )
    )

  def updateSDCell(self, sd, x, y, value, shift):
    return self.updateSD(
      sd, 
      value, 
      shift + SD_CELL_START_SHIFT + ( self.getCellByXY(x, y) * SD_CELL_SHIFT )
    )
  
  def updateSD(self, sd, value, shift):
    return (sd | ( value << shift ) )
  
  def addSD(self, sd, value, shift):
    return (sd + ( value << shift ) )

  def getOrderedFriendLocations(self):
    return sorted(self.__class__.friends, key=lambda x: x[0]+x[1])
  
  # Returns the number of friends (not self)
  # that are near to the coordinate
  def getHotspotValue(self, coord):
    if coord is None:
      return None
    counter = 0
    for friend in self.observation.friends:
      if point_dist(friend[0:2], coord) < HOTSPOT_RANGE:
        counter += 1
    return counter;

  # Returns the number of agents
  # that are going to the coordinate
  def getTrendingSpotValue(self, coord):
    if coord is None:
      return None
    if coord in self.__class__.trendingSpot:
      return len(self.__class__.trendingSpot[coord])
    else:
      return 0

  # Returns a metric representing the
  # amount of crowdness in the coordinate.
  # Uses both trendingSpot and hotspot values.
  def getCrowdedValue(self, coord):
    if coord is None:
      return None
    return self.getTrendingSpotValue(coord) + self.getHotspotValue(coord)

  # Returns the percentage of turns that the
  # control point has been in friendly hands
  def getDominationValue(self, cp):
    if cp is None:
      return None
    if cp in self.__class__.inFriendlyHands:
      return self.__class__.inFriendlyHands[cp]/float(self.observation.step)
    else:
      return 0

  # Returns the closest enemy that can be shot at
  # or None if none is reachable
  def getClosestEnemyInFireRange(self):
    obs = self.observation
    if obs.foes:
      closest_foe = self.getClosestLocation(obs.foes)[0:2]
      if(
        point_dist(closest_foe, obs.loc) < self.settings.max_range
        and not line_intersects_grid(obs.loc, closest_foe, self.grid, self.settings.tilesize)
      ):
        return closest_foe
    return None

  # BETA!
  # Safety score based on:
  # - distance to home base
  # - domination value
  # - hotspot value
  # - distance to a known ammo spot
  def getSafetyScore(self, coord):
    return (
      self.getDominationValue(coord)
      + self.getCrowdedValue(coord)
      - point_dist(coord, self.__class__.home_base)
      - min(map(lambda x: point_dist(coord, x), self.__class__.ammoSpots))
    )

  # Returns the control points
  # that are in enemy hands
  def getEnemyCPs(self):
    return filter(lambda x: x[2] != self.team, self.observation.cps)

  # Returns the control points
  # that are in enemy hands
  # and have a low crowdedValue
  def getQuietEnemyCPs(self):
    return filter((lambda x: x[2] != self.team and
      self.getCrowdedValue(x[0:2]) < CROWDED_HOTSPOT), self.observation.cps)

  # Returns the control points
  # that are in friendly hands
  def getFriendlyCPs(self):
    return filter(lambda x: x[2] == self.team, self.observation.cps)

  # Returns the control points
  # that are in enemy hands
  # and have a low crowdedValue
  def getQuietFriendlyCPs(self):
    return filter(( lambda x: x[2] == self.team and 
      self.getCrowdedValue(x[0:2]) < CROWDED_HOTSPOT), self.observation.cps)
  
  def getQuietAmmoSpots(self):
    return filter(( lambda x: self.getCrowdedValue(x[0:2]) < 1), self.ammoSpots)
  
  # Returns the control points
  # that are in enemy hands
  # and have a low crowdedValue
  # and domination value
  def getQuietRestlessFriendlyCPs(self):
    return filter(( lambda x: x[2] == self.team and 
      self.getDominationValue(x[0:2]) < DOMINATION_THRESHOLD and
      self.getCrowdedValue(x[0:2]) < CROWDED_HOTSPOT), self.observation.cps)

 # Returns if the line from self.loc to goal
  # intersects one of the friends.
  def isFriendInWay(self, goal):
    if goal is None:
        return False
    start = self.observation.loc
    friends = map(lambda x:x[0:2], self.observation.friends)
    for ally in friends:
      friend_in_way = line_intersects_circ(start,goal,ally,8) #radius = 8
      if friend_in_way:
        self.debugMsg("Shooting from (%d, %d)" % start)
        self.debugMsg("Shooting to (%d, %d)" % goal)
        self.debugMsg("Trying to shoot a friend! (%d,%d)" % ally)
        return True
    return False

  def getClosestLocation(self, locations):
    """ Returns the closest location from the set
        in terms of point distance to the current location
    """
    obs = self.observation
    if len(locations) > 0:
      min_i = 0
      min_dist = point_dist(obs.loc, locations[0][0:2])
      for i in xrange(1, len(locations)):
        dist = point_dist(obs.loc, locations[i][0:2])
        if dist < min_dist:
          min_i = i
          min_dist = dist
      return locations[min_i][0:2]
    else:
      return None

  # Returns the nearest neighbour of
  # target in the set sdSet
  def getSDNN(self, sdSet, target):
    dist = 'Inf'
    nn = None
    for sd in sdSet:
      d = self.getSDdist(sd,target)
      if d < dist:
        nn = sd
        dist = d
    return nn

  def getSDdist(self, sd1, sd2):
    delta = sd1^sd2
    dist = 0
    for i in xrange(delta.bit_length()):
      dist += ((delta >> i) & 1)
      
  #####################################
  # *  Action and strategy methods  * #
  #####################################
  
  def action(self):
    """ This function is called every step and should
        return a tuple in the form: (turn, speed, shoot)
    """
    obs = self.observation

    action = None
    try:
      if SETTINGS_DEAD_CANT_THINK and obs.respawn_in > -1:
        self.debugMsg("Sleeping")
        return (0,0,False)

      # Check if agent reached goal.
      if self.goal and point_dist(self.goal, obs.loc) < self.settings.tilesize:
        self.goal = None

      # If agent already has a goal
      # check if the motivation is still accurate
      if self.goal:
        self.validateMotivation()

      # Drive to where the user clicked
      if self.selected and self.observation.clicked:
        self.motivation = MOTIVATION_USER_CLICK
        self.goal = obs.clicked

      if self.goal is None:
        game_strats = GAME_STATE_STRATEGIES[self.game_state]
        action = ROLES[game_strats[self.id]](self)()
        
        '''
        if self.id == 0 or self.id == 1:        
          action = ROLES["O"](self)() # Offence
        elif self.id == 2 or self.id == 3:
          action = ROLES["D"](self)() # Defence
        else:
          action = ROLES["N"](self)() # Normal
        '''
       
      else:
        self.debugMsg("Goal already found: (%d,%d)" % self.goal)
    except Exception:
      self.goal = None
#      self.debugMsg("Goal: %s, exception: %s" % (self.goal, exp), True)
    
    if self.goal is None:
      self.goal = obs.loc

    self.updateTrendingSpot()
    if action is None:
      if self.goal == obs.loc:
        return (0,0,False)
      else:
        return self.getActionTriple()
    else:
      return action
  
  def action_offence(self):
    ######################
    #  STRATEGY OFFENCE  #
    ######################################
    # 1) Shoot live enemies              #
    # 2) Make sure you have ammo         #
    # 3) Move close to enemy base        #
    # 4) Go to nearby ammo spot          #
    # 5) Wait for enemies to come alive  #
    ######################################
    obs = self.observation
    eb = self.__class__.enemy_base
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    self.debugMsg("Offence strategy")
    
    # Attack strategy 1
    #########################
    # 1) Shoot live enemies #
    #########################
    # Aim at the closest enemy outside the enemy base
    if obs.ammo > 0 and obs.foes:
      living = filter(lambda x: point_dist(x[0:2], eb) > ENEMY_BASE_RANGE, obs.foes)
      self.debugMsg("Living: %s" % (living,))
      if living:
        self.goal = min(living, key=lambda x: point_dist(obs.loc, x[0:2]))[0:2]
        self.motivation = MOTIVATION_SHOOT_TARGET
        # Check if enemy in fire range
        if (
          point_dist(self.goal, obs.loc) < self.settings.max_range and
          not line_intersects_grid(
            obs.loc, 
            self.goal, 
            self.grid, 
            self.settings.tilesize
          )
          and not self.goal in self.__class__.hitlist
          and not self.isFriendInWay(self.goal)
        ):
          self.debugMsg("*> Shoot (%d,%d)" % self.goal)
          self.__class__.hitlist.append(self.goal)
          #return self.getActionTriple(True,None,0) ###?? SHOULD WE STOP MOVING WHEN WE SHOOT?
          return self.getActionTriple(True)
        else:
          return self.getActionTriple()    
    
    #ONLY GO TO AMMO PACKS THAT ARE NOT BEGIN VISITED BY TEAM
    feasible_ammo_spots = self.getQuietAmmoSpots()
    ##############################
    # 2) Make sure you have ammo #
    ##############################
    if obs.ammo < SUFFICIENT_AMMO:
      self.debugMsg("Need to recharge ammo")
      self.goal = self.getClosestLocation(ammopacks)
      # If you see a ammo pack nearby, take it
      if self.goal:
        self.debugMsg("*> Recharge (%d,%d)" % (self.goal[0],self.goal[1]))
        self.motivation = MOTIVATION_AMMO
        return self.getActionTriple()
      # Else go to a known ammo spot
      elif self.ammoSpots:
        # If you are already on an ammo spot, stay put.
        if obs.loc in self.ammoSpots:
          self.goal = obs.loc
          self.motivation = MOTIVATION_STAY_PUT
          return (0,0,False)
        # Else go to a nearby ammo spot
        elif feasible_ammo_spots:
          self.goal = self.getClosestLocation(feasible_ammo_spots)
          self.motivation = MOTIVATION_AMMO
          return self.getActionTriple()
      else:
        self.goal = obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
        self.__class__.joint_goal.append(self.goal)
        self.debugMsg("*> Walking random (%d,%d)" % self.goal)
        return self.getActionTriple()
    
    
    ###############################
    # 3) Move close to enemy base #
    ###############################  
    if eb:
      dist_to_enemy_base = point_dist(eb, obs.loc)
      
      if dist_to_enemy_base > 3 * ENEMY_BASE_RANGE: 
        #stand a little outside enemy base
        self.goal = eb
        self.motivation = MOTIVATION_ENEMY_BASE
        return self.getActionTriple()
      
    #############################
    # 4) Go to nearby ammo spot #
    #############################
    if self.goal not in self.ammoSpots: 
      nearest_ammo = self.getClosestLocation(self.ammoSpots)
      self.goal = nearest_ammo
      self.motivation = MOTIVATION_AMMO
      return self.getActionTriple()

    #####################################
    # 5) Wait for enemies to come alive #
    #####################################
    else:
      self.debugMsg("*> All enemies are probably dead")
      self.goal = self.__class__.enemy_base
      self.motivation = MOTIVATION_STAY_PUT
      return self.getActionTriple(False, None, 0)

  def action_defend(self):
    obs = self.observation
    shoot = False
    eb = self.__class__.enemy_base

    #########################
    # 1) Shoot live enemies #
    #########################
    # Aim at the closest enemy outside the enemy base
    if obs.ammo > 0 and obs.foes:
      living = filter(lambda x: point_dist(x[0:2], eb) > ENEMY_BASE_RANGE, obs.foes)
      self.debugMsg("Living: %s" % (living,))
      if living:
        self.goal = min(living, key=lambda x: point_dist(obs.loc, x[0:2]))[0:2]
        self.motivation = MOTIVATION_SHOOT_TARGET
        # Check if enemy in fire range
        if (
          point_dist(self.goal, obs.loc) < self.settings.max_range and
          not line_intersects_grid(
            obs.loc, 
            self.goal, 
            self.grid, 
            self.settings.tilesize
          )
          and not self.goal in self.__class__.hitlist
          and not self.isFriendInWay(self.goal)
        ):
          self.debugMsg("*> Shoot (%d,%d)" % self.goal)
          self.__class__.hitlist.append(self.goal)
          #return self.getActionTriple(True,None,0) ###?? SHOULD WE STOP MOVING WHEN WE SHOOT?
          return self.getActionTriple(True)
        else:
          return self.getActionTriple()    

    # If there isn't sufficient ammo
    # and there is ammo around
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    if ammopacks and obs.ammo < SUFFICIENT_AMMO:
        self.goal = self.getClosestLocation(ammopacks)
        self.motivation = MOTIVATION_AMMO

    # If no goal was set.
    if self.goal is None:
      # If there is enough ammo and there are friendly CPs
      if obs.ammo >= SUFFICIENT_AMMO and len(self.__class__.friendlyCPs) > 1:
        # Guard the control point with the lowest
        # domination value
        self.debugMsg(1)
        self.debugMsg(self.joint_goal)
        self.debugMsg(2)
        min_x = 100
        for x in self.friendlyCPs:
          d = self.getDominationValue(x)
          if d < min_x and x not in self.joint_goal:
            min_x = d
            self.goal = x
            
        '''self.goal = min(
          self.__class__.friendlyCPs,
          key=lambda x: self.getDominationValue(x) if x not in self.joint_goal else None,
        )'''
        self.__class__.joint_goal.append(self.goal)
        self.debugMsg(self.joint_goal)
        self.motivation = MOTIVATION_GUARD_CP
      # If there is not enough ammo and there are known ammo spots,
      # wait on the ammo spot.
      elif self.ammoSpots and obs.ammo < SUFFICIENT_AMMO:
        feasible_ammo_spots = self.getQuietAmmoSpots()
        if feasible_ammo_spots:
          self.goal = self.getClosestLocation(self.getQuietAmmoSpots())
          self.debugMsg("*> Waiting on ammospot (%d,%d)" % (self.goal[0],self.goal[1]))
          self.motivation = MOTIVATION_AMMO_SPOT
        else:
          self.goal = obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
          self.debugMsg("*> Walking random (%d,%d)" % self.goal)
      # Else go to a random control point
      else:
        self.goal = obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
        self.debugMsg("*> Walking random (%d,%d)" % self.goal)
    if self.goal:
      return self.getActionTriple(shoot)
    else:
      return (0,0,shoot)

  def action_normal(self):
    """ This function is called every step and should
        return a tuple in the form: (turn, speed, shoot)
    """
    obs = self.observation
    shoot = False
    eb = self.__class__.enemy_base
    
    #########################
    # 1) Shoot live enemies #
    #########################
    # Aim at the closest enemy outside the enemy base
    if obs.ammo > 0 and obs.foes:
      living = filter(lambda x: point_dist(x[0:2], eb) > ENEMY_BASE_RANGE, obs.foes)
      self.debugMsg("Living: %s" % (living,))
      if living:
        self.goal = min(living, key=lambda x: point_dist(obs.loc, x[0:2]))[0:2]
        self.motivation = MOTIVATION_SHOOT_TARGET
        # Check if enemy in fire range
        if (
          point_dist(self.goal, obs.loc) < self.settings.max_range and
          not line_intersects_grid(
            obs.loc, 
            self.goal, 
            self.grid, 
            self.settings.tilesize
          )
          and not self.goal in self.__class__.hitlist
          and not self.isFriendInWay(self.goal)
        ):
          self.debugMsg("*> Shoot (%d,%d)" % self.goal)
          self.__class__.hitlist.append(self.goal)
          #return self.getActionTriple(True,None,0) ###?? SHOULD WE STOP MOVING WHEN WE SHOOT?
          return self.getActionTriple(True)
        else:
          return self.getActionTriple()    

    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    if ammopacks:
      self.updateAllAmmoSpots(ammopacks)
      # Walk to ammo
      if obs.ammo < SUFFICIENT_AMMO:
        self.goal = self.getClosestLocation(ammopacks)
        self.motivation = MOTIVATION_AMMO
        self.debugMsg("*> Recharge (%d,%d)" % (self.goal[0],self.goal[1]))
          
    # Walk to an enemy CP
    if self.goal is None:
      self.goal = self.getClosestLocation(self.getQuietEnemyCPs())
      if self.goal:
        self.debugMsg("Crowded location: %d" % self.getCrowdedValue(self.goal))
        self.motivation = MOTIVATION_CAPTURE_CP
        self.debugMsg("*> Capture (%d,%d)" % (self.goal[0],self.goal[1]))
        
    if self.goal is None:
      self.goal = max(
          self.__class__.ammoSpots,
          key=lambda x: point_dist(x, obs.loc),
        )
      self.debugMsg("Going to ammospot far away (%d, %d)" % (self.goal[0],self.goal[1]))
      self.motivation = MOTIVATION_STAY_PUT
      

    if self.goal:
      return self.getActionTriple(shoot)
    else:
      return self.getActionTriple(shoot)

  # Checks if the current motivation to
  # go to the goal is still valid.
  # If not, it clears the goal and motivation
  def validateMotivation(self):
    obs = self.observation
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    self.debugMsg("[MOT: %s]" % (self.motivation,))
    if obs.foes or ammopacks:
      self.goal = None
    elif self.motivation == MOTIVATION_CAPTURE_CP:
      # If the CP to be captures is already friendly, stop.
      if (
          self.goal in self.__class__.friendlyCPs or
          self.getCrowdedValue(self.goal) >= CROWDED_HOTSPOT
      ):
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_GUARD_CP:
      if len(self.__class__.enemyCPs) > 1:
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_AMMO:
      if ((self.goal[0], self.goal[1], 'Ammo') not in obs.objects):
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_ENEMY_BASE:
      if self.strategy == STRATEGY_OFFENCE:
        cfoe = self.getClosestEnemyInFireRange()
        if cfoe:
          self.goal = None
          self.motivation = None
    elif self.motivation == MOTIVATION_AMMO_SPOT:
      if self.getClosestLocation(self.ammoSpots) != self.goal:
        self.goal = self.getClosestLocation(self.ammoSpots)
        self.motivation == MOTIVATION_AMMO_SPOT
    elif self.motivation == MOTIVATION_SHOOT_TARGET:
      if self.goal not in map(lambda x: x[0:2], obs.foes):
        self.goal = None
        self.motivation = None
      
  '''def action(self):
    obs = self.observation
    try:
      if Q_LEARNING:
        if self.id == 0:
          state = self.getStateDescription()
          if (random.random() < EPSILON) or (self.__class__.PREVIOUS_STATE == None) or (state not in Q):
            #assign random action
            action = []
            for i in xrange(6):
              action.append(ROLES_SET[random.randint(0,len(ROLES_SET)-1)])
            action = tuple(action)
          else:
            #assign action according to policy
            action = max(Q[state], key=lambda x: Q[state][x])
          # check if the Q values are instantiated, else the value is 0
          if state not in Q:
            Q[state] = {action:0}
          elif action not in Q[state]:
            Q[state][action] = 0
             
          ALPHA = 1.0 - (self.observation.step/666.666)
          
          if self.PREVIOUS_STATE != None:
            Q[self.PREVIOUS_STATE][self.PREVIOUS_ACTION] = (
              Q[self.PREVIOUS_STATE][self.PREVIOUS_ACTION] +
              ALPHA *
              (
                self.PREVIOUS_REWARD +
                GAMMA *
                (
                  Q[state][action] - Q[self.PREVIOUS_STATE][self.PREVIOUS_ACTION]
                )
              )
            )
          
          self.__class__.PREVIOUS_STATE = state
          self.__class__.PREVIOUS_ACTION = action
          self.__class__.PREVIOUS_REWARD = 0
          
        fs = self.getOrderedFriendLocations()
        
        for f in xrange(len(fs)):
          if fs[f][3] == self.id:
            if self.a != None:
              self.a_previous = self.a
              self.a = self.PREVIOUS_ACTION[f]
            else:
              self.a = self.PREVIOUS_ACTION[f]
              self.a_previous = self.a
              
        # Check if agent reached goal.
        if self.goal and point_dist(self.goal, obs.loc) < self.settings.tilesize:
          self.goal = None

        # If agent already has a goal
        # check if the motivation is still accurate
        if self.goal:
          self.validateMotivation()
        
        self.debugMsg("[Role] %s" % (self.a,))

        if self.goal is None:
          # Execute role
          self.debugMsg("Execute role")
          action = ROLES[self.a](self)()
          self.debugMsg("Role executed")
        else:
          action = getAggressiveAction()
      else:
        # Check if agent reached goal.
        if self.goal and point_dist(self.goal, obs.loc) < self.settings.tilesize:
          self.goal = None

        # If agent already has a goal
        # check if the motivation is still accurate
        if self.goal:
          self.validateMotivation()
        
        if self.goal is None:
          if self.id == 0 or self.id == 1:        
            action = ROLES["O"](self)() # Offence
          elif self.id == 2 or self.id == 3:
            action = ROLES["D"](self)() # Defence
          else:
            action = ROLES["N"](self)() # Normal
        else:
          action = getAggressiveAction()
    except Exception, ex:
      self.debugMsg("Goal: %s, exception: %s" % (self.goal, exp), True)
      self.goal = None

    if self.goal is None:
      self.goal = obs.loc

    self.updateTrendingSpot()
    if action is None:
      if self.goal == obs.loc:
        return (0,0,False)
      else:
        return self.getAggressiveAction()
    else:
      return action

  def action_offence(self):
    ######################
    #  STRATEGY OFFENCE  #
    ######################################
    # 1) Make sure you have ammo         #
    # 2) Move close to enemy base        #
    # 3) Shoot live enemies              #
    # 4) Go to nearby ammo spot          #
    # 5) Wait for enemies to come alive  #
    ######################################
    obs = self.observation
    self.debugMsg("Offence strategy")

    ##############################
    # 1) Make sure you have ammo #
    ##############################
    if obs.ammo < SUFFICIENT_AMMO:
      self.role_getAmmo()

    ###############################
    # 2) Move close to enemy base #
    # and shoot anyone on the way #    
    ###############################  
    eb = self.__class__.enemy_base;
    if eb is not None:
      dist_to_enemy_base = point_dist(eb, obs.loc)
      
      if dist_to_enemy_base > 3 * ENEMY_BASE_RANGE: 
        #stand a little outside enemy base
        self.goal = eb
        self.motivation = MOTIVATION_ENEMY_BASE
        return self.getAggressiveAction()

    #############################
    # 4) Go to nearby ammo spot #
    #############################
    self.goal = self.getClosestLocation(self.getQuietAmmoSpots())
    if self.goal is not None:
      self.motivation = MOTIVATION_AMMO
      return self.getAggressiveAction()
    else:
      return self.goToRandomCP()
    
  def action_defend(self):
    obs = self.observation
    
    # If there isn't sufficient ammo and there is ammo around, get it
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    if ammopacks and obs.ammo < SUFFICIENT_AMMO:
        self.goal = self.getClosestLocation(ammopacks)
        self.motivation = MOTIVATION_AMMO
    
    #if agent has a goal, previously defined or 
    #above mentioned ammopack: continue towards that goal
    if self.goal is not None:
      return self.getAggressiveAction()
    
    # If no goal was set.
    # If there is enough ammo and there are friendly CPs
    if obs.ammo >= SUFFICIENT_AMMO and len(self.__class__.friendlyCPs) >= 1:
      # Find the closest control point
      self.goal = self.getClosestLocation(self.__class__.friendlyCPs)
      # If the closest control point has a low domination value, guard it.
      # Else guard the control point with the lowest domination value
      if self.getDominationValue(self.goal) > 0.7:
        self.goal = min(
          self.__class__.friendlyCPs,
          key=lambda x: self.getDominationValue(x),
        )
      self.motivation = MOTIVATION_GUARD_CP
      return self.getAggressiveAction()
    
    # If there is not enough ammo and there are known ammo spots,
    # wait on the ammo spot.
    elif self.ammoSpots and obs.ammo < SUFFICIENT_AMMO:
      return self.role_getAmmo()
    # Else try to capture a CP
    else:
      return self.goToRandomCP()

  def action_normal(self):
    """ This function is called every step and should
        return a tuple in the form: (turn, speed, shoot)
    """
    obs = self.observation
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    if ammopacks:
      return self.role_getAmmo()
      
    # Walk to an enemy CP
    self.motivation = MOTIVATION_CAPTURE_CP
    
    #Go to Quiet enemy CP
    self.goal = self.getClosestLocation(self.getQuietEnemyCPs())
    if self.goal is not None:
      self.debugMsg("Going to quiet location: %d" % self.getCrowdedValue(self.goal))
      self.debugMsg("*> Capture (%d,%d)" % (self.goal[0],self.goal[1]))
      return self.getAggressiveAction()
    
    #Go to crowded enemy CP
    self.goal = self.getClosestLocation(self.getEnemyCPs())
    if self.goal is not None:
      self.debugMsg("Going to crowded CP")
      self.debugMsg("*> Capture (%d,%d)" % (self.goal[0],self.goal[1]))
      return self.getAggressiveAction()
    
    # If you can't think of anything to do
    # got to a friendly CP
    self.goal = self.getClosestLocation(self.getQuietRestlessFriendlyCPs())
    if self.goal is not None:
      self.motivation = MOTIVATION_GUARD_CP
      self.debugMsg("*> Guard (%d,%d)" % (self.goal[0],self.goal[1]))
      return self.getAggressiveAction()
    else:
      return self.goToRandomCP()
     
  def role_captureCP(self):
    #Role: Capture an enemy CP, no matter what - but with preference for quiet CP
    #if there is no enemy CP: go to nearest friendly CP
    self.motivation = MOTIVATION_CAPTURE_CP
    self.goal = self.getClosestLocation(self.getQuietEnemyCPs())
    if self.goal is None:
      self.goal = self.getClosestLocation(self.enemyCPs)
    if self.goal is None:
      self.goal = self.getClosestLocation(self.friendlyCPs)    
      self.motivation = MOTIVATION_GUARD_CP
    return self.getActionTriple()

  def role_getAmmo(self):
    #Role: action preferences 
    #1. Go to visible ammo
    #2. Go to other ammo spot
    #3. walk around at random (do not sit and wait for ammo!)
    obs = self.observation
    self.debugMsg("Role assignment  = recharge ammo")
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    self.goal = self.getClosestLocation(ammopacks)
    
    # If you see a ammo pack nearby, take it
    if self.goal is not None:
      self.debugMsg("*> Recharge (%d,%d)" % (self.goal[0],self.goal[1]))
      self.motivation = MOTIVATION_AMMO
      return self.getAggressiveAction()
    
    # Else go to a known ammo spot
    if self.__class__.ammoSpots:
      feasible_ammo_spots = self.getQuietAmmoSpots()
      if feasible_ammo_spots:
        self.debugMsg("going to feasible ammo spot")
        self.goal = self.getClosestLocation(feasible_ammo_spots)
        self.motivation = MOTIVATION_AMMO
        return self.getAggressiveAction()
      #else:
      #  self.goal = self.getClosestLocation(self.ammoSpots)
      #  self.debugMsg("Going to ammo spot")
      #  self.motivation = MOTIVATION_AMMO
      #  return self.getAggressiveAction()
    return self.goToRandomCP()

  def goToRandomCP(self):
      self.goal = self.observation.cps[random.randint(0,len(self.observation.cps)-1)][0:2]
      self.motivation = MOTIVATION_IDLE
      self.debugMsg("*> Going to random CP (%d,%d)" % self.goal)
      return self.getAggressiveAction()

  def role_chaseNearestEnemy(self):
    #role:
    #1. get ammo
    #2. if agent has target: go to target (small chance that this will work)
    #3. if there are enemies nearby: acquire target
    #4. else: go to enemy base
    obs = self.observation
    eb = self.__class__.enemy_base
    self.debugMsg("chasing enemy()")
    if obs.ammo < 1:
      self.debugMsg("no ammo")
      return self.role_getAmmo()
    else:
      self.debugMsg("got ammo")
      #already got a target, so try to shoot
      if self.goal is not None and self.goal in obs.foes:
        action = self.tryToShoot()
        if action is not None:
          return action
        else:
          return self.getActionTriple()
      
      #otherwise: go to enemy base and shoot every enemy on the way
      if point_dist(self.observation.loc, eb) > (3 * ENEMY_BASE_RANGE):
        self.goal = self.eb #Go to enemy base?
        self.motivation = MOTIVATION_ENEMY_BASE
        self.debugMsg("going to enemy base")
        return self.getAggressiveAction()
      else:
        return self.role_getAmmo()
    
  def tryToShoot(self):
    obs = self.observation
    if (
      point_dist(self.goal, obs.loc) < self.settings.max_range and
      not line_intersects_grid(obs.loc,self.goal, self.grid,self.settings.tilesize)
      and not self.goal in self.__class__.hitlist
      and not self.isFriendInWay(self.goal)
      ):
      self.__class__.hitlist.append(self.goal)
      return self.getActionTriple(True)
    else:
      return None
  def getAggressiveAction(self):
    old_goal = self.goal
    old_motivation = self.motivation
    obs = self.observation
    eb = self.__class__.enemy_base
    if (self.observation.ammo > 0 and self.observation.foes):
      living = filter(lambda x: point_dist(x[0:2], eb) > ENEMY_BASE_RANGE, obs.foes)
      if living:
        self.goal = min(living, key=lambda x: point_dist(obs.loc, x[0:2]))[0:2]
        self.debugMsg("*> Go to enemy (%d,%d)" % self.goal)
        self.motivation = MOTIVATION_SHOOT_TARGET
        action = self.tryToShoot()
      if action is not None:
        return action
      else:
        self.goal = old_goal
        self.motivation = old_motivation
        return self.getActionTriple()
    else:
      return self.getActionTriple()
    
  def role_flee(self):
    #Role:
    #if there are no enemies: walk around random (or capture CP?)
    #if there is one enemy in sight: move in opposite direction
    #if there are more enemies in sight: move in opposite direction of leftmost and rightmost enemy
    obs = self.observation
    if(len(obs.foes) == 0):
      self.goal = obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
      self.motivation = MOTIVATION_IDLE
      self.debugMsg("*> Walking random (%d,%d)" % self.goal)
      return self.getAggressiveAction() #moet agent hier wel of niet schieten?
      #alternative:
      #return self.role_captureCP()
    elif len(obs.foes == 1):
      foe = obs.foes[0]
      dx = foe[0] - obs.loc[0]
      dy = foe[1] - obs.loc[1]
      self.goal = (obs.loc[0] - dx, obs.loc[1] - dy)
      self.motivation = MOTIVATION_FLEE
      return self.getAggressiveAction()#moet agent hier wel of niet schieten?
    else: #two or more foes in sight
      foe1 = obs.foes[0]
      foe2 = obs.foes[1]
      #get leftmost and rightmost foes
      for foe in obs.foes:
        if(foe[0] < foe1[0]):
          foe1 = foe
        if foe[0] > foe2[0]:
          foe2 = foe
      #compute enemy front
      dx_foes = foe2[0] - foe1[0]
      dy_foes = foe2[1] - foe1[1]
      dx = dx_foes - obs.loc[0]
      dy = dy_foes - obs.loc[1]
      #move in opposite direction of enemy front
      self.goal = (obs.loc[0] - dx, obs.loc[1] - dy)
      self.motivation = MOTIVATION_FLEE
      return self.getAggressiveAction()   #moet agent hier wel of niet schieten

  # Checks if the current motivation to
  # go to the goal is still valid.
  # If not, it clears the goal and motivation
  def validateMotivation(self):
    obs = self.observation
    ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
    self.debugMsg("[MOT: %s]" % (self.motivation,))
    if obs.foes or ammopacks:
      self.goal = None
    elif self.motivation == MOTIVATION_CAPTURE_CP:
      # If the CP to be captures is already friendly, stop.
      if (
          self.goal in self.__class__.friendlyCPs or
          self.getCrowdedValue(self.goal) >= CROWDED_HOTSPOT
      ):
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_GUARD_CP:
      if len(self.__class__.enemyCPs) > 1:
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_AMMO:
      if ((self.goal[0], self.goal[1], 'Ammo') not in obs.objects):
        self.goal = None
        self.motivation = None
    elif self.motivation == MOTIVATION_ENEMY_BASE:
      if self.strategy == STRATEGY_OFFENCE:
        cfoe = self.getClosestEnemyInFireRange()
        if cfoe:
          self.goal = None
          self.motivation = None
    elif self.motivation == MOTIVATION_AMMO_SPOT:
      if self.getClosestLocation(self.ammoSpots) != self.goal:
        self.goal = self.getClosestLocation(self.ammoSpots)
        #self.motivation == MOTIVATION_AMMO_SPOT #seems a redundant statement
    elif self.motivation == MOTIVATION_SHOOT_TARGET:
      if self.goal not in map(lambda x: x[0:2], obs.foes):
      #if self.goal in self.observation.hit:
        self.goal = None
        self.motivation = None'''
        
  # Return action triple
  # Possible to preset shoot, turn or speed
  def getActionTriple(self,shoot=False,turn=None,speed=None):
    obs = self.observation
    if turn is None or speed is None:
      # Compute path, angle and drive
      path = find_path(
        obs.loc, 
        self.goal, 
        self.mesh, 
        self.grid, 
        self.settings.tilesize
      )
      if path:
        dx = path[0][0]-obs.loc[0]
        dy = path[0][1]-obs.loc[1]
        
        if speed is None:
          speed = (dx**2 + dy**2)**0.5

        turn = angle_fix(math.atan2(dy, dx)-obs.angle)
        if turn > self.settings.max_turn or turn < -self.settings.max_turn:
          shoot = False
          speed *= 0.25

      else:
        turn = 0
        speed = 0

    #Don't shoot if target is too far away
    if shoot == True and point_dist(obs.loc,self.goal) > self.settings.max_range:
      shoot = False
    # Don't shoot if a friend is in the way
    if shoot == True and SETTINGS_DONT_HARM_FRIENDS:
      if self.isFriendInWay(self.goal):
        shoot = False
    return (turn,speed,shoot)

  #####################
  # * Debug methods * #
  #####################

  # Write a debug message to 
  # the agent's log file
  def debugMsg(self, msg, error=False):
    if SETTINGS_DEBUG_ON:
      if hasattr(self, 'observation'):
        if error:
          self.log.write(
            "[%d-%f|!ERROR!]: %s\n" % (self.observation.step, time.time(), msg))
        elif not SETTINGS_DEBUG_ERROR_ONLY:
          self.log.write(
            "[%d-%f]: %s\n" % (self.observation.step, time.time(), msg))
      else:
        if error:
          self.log.write(
            "[?-%f|!ERRROR!]: %s\n" % (time.time(), msg))
        elif not SETTINGS_DEBUG_ERROR_ONLY:
          self.log.write(
            "[?-%f]: %s\n" % (time.time(), msg))
      self.log.flush()

  def debug(self, surface):
    """ Allows the agents to draw on the game UI,
        Refer to the pygame reference to see how you can
        draw on a pygame.surface. The given surface is
        not cleared automatically. Additionally, this
        function will only be called when the renderer is
        active, and it will only be called for the active team.
    """
    import pygame
    if self.id == 0:
      # First agent clears the screen
      surface.fill((0,0,0,0))
      if SETTINGS_DEBUG_ON:
        if SETTINGS_DEBUG_SHOW_DOMINATION:
          self._drawCPDomination(pygame, surface)
        if SETTINGS_DEBUG_SHOW_BASES:
          self._drawBases(pygame, surface)
        if SETTINGS_DEBUG_SHOW_KNOWN_AMMO_SPOTS:
          self._drawKnownAmmoSpots(pygame, surface)

      if SETTINGS_DEBUG_SHOW_GRIDLINES:
        self._drawGridLines(pygame, surface)
  
    # Selected agents draw their info
    if self.selected:
      if self.goal:
        pygame.draw.line(surface,(0,0,0),self.observation.loc, self.goal)
      if SETTINGS_DEBUG_ON:
        if SETTINGS_DEBUG_SHOW_VISIBLE_OBJECTS:
          self._drawVisibleObjects(pygame, surface)
        if SETTINGS_DEBUG_SHOW_VISIBLE_FOES:
          self._drawVisibleFoes(pygame, surface)
        self._drawDebugTextSurface(pygame, surface)

  #################
  #    Private    #
  # debug methods #
  #################

  def _drawGridLines(self, pygame, surface):
    font = pygame.font.Font(pygame.font.get_default_font(), 10)
    for x in xrange(0, self.field_width, GRID_SIZE):
      pygame.draw.line(surface, (255,0,0), (x,0), (x, self.field_height))
      txt = font.render("%d" % (x/GRID_SIZE), True, (255,0,0))
      surface.blit(txt, (x+2, 2))
    for y in xrange(0, self.field_height, GRID_SIZE):
      pygame.draw.line(surface, (255,0,0), (0,y), (self.field_width,y))
      txt = font.render("%d" % (y/GRID_SIZE), True, (255,0,0))
      surface.blit(txt, (2, y+2))

  def _drawVisibleFoes(self, pygame, surface):
    for o in self.observation.foes:
      pygame.draw.line(surface, (127,127,127), self.observation.loc, o[0:2])
    cfoe = self.getClosestEnemyInFireRange()
    if cfoe:
      pygame.draw.line(surface, (255,0,0), self.observation.loc, cfoe[0:2])
  
  def _drawVisibleObjects(self, pygame, surface):
    for o in self.observation.objects:
      pygame.draw.line(surface, (255,255,255), self.observation.loc, o[0:2])

  def _drawCPDomination(self, pygame, surface):
    font = pygame.font.Font(pygame.font.get_default_font(), 10)
    for cp in self.observation.cps:
      txt = font.render(
        "%.2f" % (self.getDominationValue(cp[0:2]),), 
        True,
        (0,0,255)
      )
      surface.blit(txt, cp[0:2])
      txt2 = font.render(
        "%d" % (self.getHotspotValue(cp[0:2]),),
        True,
        (255,0,0)
      )
      surface.blit(txt2, (cp[0], cp[1]+10))
      pygame.draw.circle(surface, (255,0,0), cp[0:2], HOTSPOT_RANGE,2)

  def _drawBases(self, pygame, surface):
    if self.__class__.home_base:
      font = pygame.font.Font(pygame.font.get_default_font(), 10)
      txt = font.render("@", False, (255,255,255))
      surface.blit(txt, self.__class__.home_base)
      surface.blit(txt, self.__class__.enemy_base)

  def _drawKnownAmmoSpots(self, pygame, surface):
    font = pygame.font.Font(pygame.font.get_default_font(), 10)
    for spot in self.__class__.ammoSpots:
      txt = font.render("*", False, (255,255,255))
      surface.blit(txt, spot)
    
  def _drawDebugTextSurface(self, pygame, surface):
    x = self.observation.loc[0]
    y = self.observation.loc[1]
    font = pygame.font.Font(pygame.font.get_default_font(), 10)
    if SETTINGS_DEBUG_SHOW_ID:
      # Draw id
      txt_id = font.render("%d" % (self.id,), True, (0,0,0))
      surface.blit(txt_id, (x+10,y-10))
    if SETTINGS_DEBUG_SHOW_MOTIVATION:
      # Draw motivation
      if self.motivation:
        txt_mot = font.render("%s/%s" % (self.a,self.motivation), True, (0,0,255))
        surface.blit(txt_mot, (x+10,y+5))
    if SETTINGS_DEBUG_SHOW_AMMO:
      # Draw ammo
      txt_ammo = font.render("%d" % (self.observation.ammo,), True, (255,0,0))
      surface.blit(txt_ammo, (x-10,y-10))

  def fetchSD(self, sd, bits, shift):
    return ( (sd & ( ((2**bits)-1) << shift )) >> shift )

  def fetchSDCell(self, sd, cell):
    return self.fetchSD(
      sd, 
      SD_CELL_SHIFT,
      ( SD_CELL_START_SHIFT + (cell*SD_CELL_SHIFT) )
    )

  def printSD(self, sd):
    self.debugMsg("#########SD########")
    self.debugMsg("Score: %s" % ( bin(self.fetchSD(sd, 3, SD_SCORE_SHIFT)), ))
    self.debugMsg("Prev roles: %s" % 
      ( bin(self.fetchSD(sd, 24, SD_PREVIOUS_ROLE)), ))
    self.debugMsg("Domination values: %s" % 
      ( bin(self.fetchSD(sd, 6, SD_DVALUECPS_SHIFT)), ))
    self.debugMsg("Friends ammo value: %s" % 
      ( bin(self.fetchSD(sd, 18, SD_FRIENDS_AMMO_SHIFT)), ))
    # For each cell:
    for c in xrange(self.getCellByXY(self.field_width, self.field_height)+1):
      cell = self.fetchSDCell(sd, c)
      self.debugMsg("\t*********Cell %d**********" % (c,))
      self.debugMsg("\tBitstream: %s" % (bin(cell),))
      self.debugMsg("\tFriends: %s" % 
        (bin(self.fetchSD(cell, 6, SD_FRIENDS_SHIFT)), ))
      self.debugMsg("\tEnemies: %s" % 
        (bin(self.fetchSD(cell, 3, SD_ENEMIES_SHIFT)), ))
      self.debugMsg("\tEnemy CPs: %s" % 
        (bin(self.fetchSD(cell, 3, SD_ENEMYCPS_SHIFT)), ))
      self.debugMsg("\tFriendly CPs: %s" % 
        (bin(self.fetchSD(cell, 3, SD_FRIENDCPS_SHIFT)), ))
      self.debugMsg("\tAmmo: %s" % 
        (bin(self.fetchSD(cell, 3, SD_AMMO_SHIFT)), ))
      self.debugMsg("\tDeath rate: %s" % 
        (bin(self.fetchSD(cell, 2, SD_DEATH_RATE)), ))
      self.debugMsg("\tEnemy base: %s" % 
        (bin(self.fetchSD(cell, 1, SD_ENEMYBASE_SHIFT)), ))
      self.debugMsg("\tHome base: %s" % 
        (bin(self.fetchSD(cell, 1, SD_HOMEBASE_SHIFT)), ))
    self.debugMsg("#######END SD######\n\n")

  ##################################################

  def finalize(self, interrupted=False):
    """ This function is called after the game ends, 
        either due to time/score limits, or due to an
        interrupt (CTRL+C) by the user. Use it to
        store any learned variables and write logs/reports.
    """
    if self.id == 0 and Q_LEARNING:
      self.Q2File()
  
  def Q2File(self):
      pickle.dump( Q, open( "trooper_blob", "wb" ) )
      
  def openQFile(self):
      Q = pickle.load( open( "troper_blob", "rb" ) )
 
AS_STRING = """
class Agent(object):
 NAME="default_agent"
 def __init__(self,id,team,settings=None,field_rects=None,field_grid=None,nav_mesh=None):
  self.id=id
  self.team=team
  self.mesh=nav_mesh
  self.grid=field_grid
  self.settings=settings
  self.goal=None
  if id==0:
   self.all_agents=self.__class__.all_agents=[]
  self.all_agents.append(self)
 def observe(self,observation):
  self.observation=observation
  self.selected=observation.selected
 def action(self):
  obs=self.observation
  if self.goal and point_dist(self.goal,obs.loc)<self.settings.tilesize:
   self.goal=None
  ammopacks=filter(lambda x:x[2]=="Ammo",obs.objects)
  if ammopacks:
   self.goal=ammopacks[0][0:2]
  if self.selected and self.observation.clicked:
   self.goal=self.observation.clicked
  if self.goal is None:
   self.goal=obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
  shoot=False
  if(obs.ammo>0 and obs.foes and point_dist(obs.foes[0][0:2],obs.loc)<self.settings.max_range and not line_intersects_grid(obs.loc,obs.foes[0][0:2],self.grid,self.settings.tilesize)):
   self.goal=obs.foes[0][0:2]
   shoot=True
  path=find_path(obs.loc,self.goal,self.mesh,self.grid,self.settings.tilesize)
  if path:
   dx=path[0][0]-obs.loc[0]
   dy=path[0][1]-obs.loc[1]
   turn=angle_fix(math.atan2(dy,dx)-obs.angle)
   if turn>self.settings.max_turn or turn<-self.settings.max_turn:
    shoot=False
   speed=(dx**2+dy**2)**0.5
  else:
   turn=0
   speed=0
  return(turn,speed,shoot)
 def debug(self,surface):
  import pygame
  if self.id==0:
   surface.fill((0,0,0,0))
  if self.selected:
   if self.goal:
    pygame.draw.line(surface,(0,0,0),self.observation.loc,self.goal)
 def finalize(self,interrupted=False):
  pass
"""
